/*****************************************************************************
Module :     HttpClient.CPP
Notices:     2007.3.1 Written by Ling, Xiao-li.  Mailto: ling.xiaoli.s@gmail.com
Description: This is a resuable simple HTTP protocol wrapper which support:
			 gzip compression method/utf-8 encode&decode/post&get data/raw cookie.
			 All functions based on the MFC class CInternetSession, so it's less dependent.
			 
*****************************************************************************/
#pragma once

// If the needed header file isn't in the stdafx.h, you should include it here.
#include <afxinet.h>
#include <comutil.h>
#include <complex>
#include "EventLog.h"

using namespace System::EventLog;

namespace System
{
	namespace Internet
	{

#define HTTP_RETRY_COUNT 5

		class CHttpClient: public CEventWrapper
		{
		public:
			CHttpClient(void);
			bool	Init() {
//				m_dwPrevtick = GetTickCount();
				unsigned long ulRetryCnt = HTTP_RETRY_COUNT;
				//BOOL bRet = m_session.SetOption(INTERNET_OPTION_CONNECT_RETRIES , &ulRetryCnt, sizeof(unsigned long), INTERNET_FLAG_DONT_CACHE);
				BOOL bRet = m_session.SetOption(INTERNET_OPTION_CONNECT_RETRIES , &ulRetryCnt, sizeof(unsigned long));
				//bRet = m_session.SetOption(INTERNET_OPTION_RESET_URLCACHE_SESSION, (LPVOID)NULL, 0);				
				return (bRet?true:false);
			};
			~CHttpClient(void);

		public:

			CString GetFullURL(LPCTSTR lpszSvr, LPCTSTR lpszURL) {
				CString sFullURL(lpszSvr);
				CString sURLPart(lpszURL);

				sFullURL.MakeLower();
				if (sFullURL.Find(_T("http://")) <0)
					sFullURL = CString(_T("http://")) + sFullURL;

				if (sFullURL.Find(_T('/'), sFullURL.GetLength()-1) <0 && 
					sURLPart.Find(_T('/'), 0) !=0)
					sFullURL += _T('/');

				sFullURL+=lpszURL;
				return sFullURL;
			};
			// lpszSvr must only contain the dns name without any protocol or directory string
			bool	GetResponse(LPCTSTR lpszSvr, LPCTSTR lpszURL, CString& sRetHTML, LPCSTR lpszPostData);	

			CString	GetRawCookie() { return m_sRawCookie;};
			void	SetRawCookie(LPCTSTR lpszCookie=NULL)	{ m_sRawCookie = lpszCookie;};

			// if failed, return empty CStringA object.
			// Maximum UTF-8 encoded string length not greater than lstrlen(lpszStr)*4
			static CStringA Encode_UTF8(LPCTSTR lpszStr) {
				CStringA sRet, sEmptyRet;
				if (lstrlen(lpszStr) <= 0) 
				{
					TRACE(
						((CString(_T("Encode_UTF8():passed in string is empty.")) + _T(__FILE__)) + _T("\t")) + CString(_variant_t(__LINE__))
						);
					return sEmptyRet;
				}

				CStringW wsStr = CT2W(lpszStr);
				long lRet = ::WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)wsStr, wsStr.GetLength(), NULL, 0, NULL, NULL);
				if (0 == lRet) {
					TRACE(
						(CString(_T("Encode_UTF8():Getting needed buffer size when converting normal string to UTF-8 encoded string failed.")) + __FILE__) + _T("\t") + CString(_variant_t(__LINE__))
						);
					return sEmptyRet;
				}

				LPSTR lpszBuf = sRet.GetBuffer(lRet+1);
				::ZeroMemory(lpszBuf, lRet+1);

				long lBufSize = lRet;
				lRet = ::WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)wsStr, wsStr.GetLength(), lpszBuf, lRet, NULL, NULL);
				if (0 == lRet) {
					TRACE(
						(CString(_T("Encode_UTF8():Converting normal string to UTF-8 encoded string failed.")) + __FILE__) + _T("\t") + CString(_variant_t(__LINE__))
						);
					return sEmptyRet;
				}
				if (lRet != lBufSize) {
					TRACE(
						(CString(_T("Encode_UTF8():Written bytes not equal to allocated buffer size for UTF-8 encoding.")) + __FILE__) + _T("\t") + CString(_variant_t(__LINE__))
						);
					return sEmptyRet;
				}

				return sRet;
			};


			// Maximum URL encoded bytes stream length not greater than (bytes stream length)*3

			/*
			// Windows API about URL encoding seems only encoding some special chars: 
			// ^&`{}|]["<>\#?/ %
			// if failed, return 0
			static long Encode_URL_Size(BYTE* lpszStr) {  // UrlEscape()
			DWORD dwSize=0;
			HRESULT hr = ::UrlEscape (lpszStr, NULL, dwSize, URL_ESCAPE_SEGMENT_ONLY | URL_ESCAPE_PERCENT);
			if (hr != E_POINTER) return 0;
			return dwSize;
			};
			static bool Encode_URL(BYTE* lpszStr, BYTE* pBuf, long lBufSize) {
			HRESULT hr = ::UrlEscape (lpszStr, pBuf, lBufSize, URL_ESCAPE_SEGMENT_ONLY | URL_ESCAPE_PERCENT);
			return i(hr == S_OK) ;
			};
			*/

		protected:
			bool ReadContent(CHttpFile* pFile, CString& sRetContent);
			bool ReadCookie(CHttpFile* pFile);
			bool ReadContentType(CHttpFile* pFile, CString& sContentType, bool& bUTF_8);

			bool FormatePostData(VARIANT& vPostData, LPCTSTR lpszPostData);

			void GetLastError(LPCTSTR lpszFuncName);

		protected:
			CInternetSession	m_session;
			CString				m_sRawCookie;	// the raw cookie string
			CString				m_sUserAgent;	// the User-Agent: string
			DWORD				m_dwPrevtick;	// store the value of the previous time of running
		};

	};
};
